package cn.easyplatform.studio.web.controller.admin;

import cn.easyplatform.studio.StudioApp;
import cn.easyplatform.studio.Version;
import cn.easyplatform.studio.cmd.admin.GetSessionManagerCmd;
import cn.easyplatform.studio.cmd.admin.MainCmd;
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.zkoss.chart.*;
import org.zkoss.chart.plotOptions.GaugePlotOptions;
import org.zkoss.util.resource.Labels;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.select.SelectorComposer;
import org.zkoss.zk.ui.select.annotation.Listen;
import org.zkoss.zk.ui.select.annotation.Wire;
import org.zkoss.zul.Label;

import java.io.File;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.RuntimeMXBean;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @Author Zeta
 * @Version 1.0
 */
public class HomeController extends SelectorComposer<Component> {

    @Wire("label#userCount")
    private Label userCount;

    @Wire("label#userRate")
    private Label userRate;

    @Wire("label#dbCount")
    private Label dbCount;

    @Wire("label#dbRate")
    private Label dbRate;

    @Wire("label#diskCount")
    private Label diskCount;

    @Wire("label#diskRate")
    private Label diskRate;

    @Wire("label#coreCount")
    private Label coreCount;

    @Wire("label#coreRate")
    private Label coreRate;

    @Wire("label#epVersion")
    private Label epVersion;

    @Wire("label#vmVersion")
    private Label vmVersion;

    @Wire("label#vmVendor")
    private Label vmVendor;

    @Wire("label#vmArgs")
    private Label vmArgs;

    @Wire("label#osName")
    private Label osName;

    @Wire("label#osArch")
    private Label osArch;

    @Wire("label#processors")
    private Label processors;

    @Wire("label#pid")
    private Label pid;

    @Wire("label#totalMemory")
    private Label totalMemory;

    @Wire("label#startTime")
    private Label startTime;

    @Wire("label#upTime")
    private Label upTime;

    @Wire("label#vmCommand")
    private Label vmCommand;

    @Wire("label#totalThread")
    private Label totalThread;

    @Wire("charts#gaugeChart")
    private Charts gaugeChart;

    @Wire("charts#cpuChart")
    private Charts cpuChart;

    @Wire("charts#memoryChart")
    private Charts memoryChart;

    private long maxMemoryUsed;

    private double maxCpuUsed;

    private com.sun.management.OperatingSystemMXBean osm;

    private MemoryMXBean mmb;

    private RuntimeMXBean rmb;

    private DruidDataSource dds;


    @Override
    public void doAfterCompose(Component comp) throws Exception {
        super.doAfterCompose(comp);

        userCount.setValue(StudioApp.execute(new GetSessionManagerCmd()).getUsers().size()+"");//当前在线用户数
        dds=(DruidDataSource) StudioApp.execute(new MainCmd());
        dbCount.setValue(dds.getActiveCount() + "");//数据库连接池
        dbRate.setValue(format(dds.getActiveCount(), dds.getMaxActive()));//数据库连接池百分比
        File file = null;
        try {
            file = File.createTempFile(RandomStringUtils.randomAlphanumeric(10), "tmp");
            long used = file.getTotalSpace() - file.getUsableSpace();
            diskCount.setValue(format(used));//系统磁盘空间
            diskRate.setValue(format(used, file.getTotalSpace()));//系统磁盘空间百分比
        } catch (Exception e) {
        } finally {
            FileUtils.deleteQuietly(file);
        }

        rmb = ManagementFactory.getRuntimeMXBean();
        osm = (com.sun.management.OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();//操作系统
        mmb = ManagementFactory.getMemoryMXBean();//内存使用率

        epVersion.setValue("V" + Version.VERSION + "  (" + Version.TIMESTAMP + ")");//ep版本

        vmVersion.setValue(rmb.getVmName() + " " + rmb.getSpecVersion() + "-" + rmb.getVmVersion());//vm版本
        vmVersion.setTooltiptext(vmVersion.getValue());
        vmVendor.setValue(rmb.getVmVendor());//vm供应商
        vmVendor.setTooltiptext(vmVendor.getValue());
        vmArgs.setValue(StringUtils.substringBetween(Arrays.toString(rmb.getInputArguments().toArray()), "[", "]"));//vm参数
        vmArgs.setTooltiptext(vmArgs.getValue());

        osName.setValue(osm.getName());//操作系统
        osName.setTooltiptext(osName.getValue());
        osArch.setValue(osm.getArch());//操作系统体系架构
        osArch.setTooltiptext(osArch.getValue());
        processors.setValue(osm.getAvailableProcessors() + Labels.getLabel("admin.jvm.code"));//处理器数量
        pid.setValue(rmb.getName().split("@")[0]);
        totalMemory.setValue(String.format("%.2fG", osm.getTotalPhysicalMemorySize() / (Math.pow(1024.0, 3.0))));//物理内存总量
        startTime.setValue(DateFormatUtils.format(rmb.getStartTime(), "yyyy-MM-dd HH:mm:ss"));//开始时间
        upTime.setValue(calculateInterval(rmb.getUptime()));//运行时间
        vmCommand.setValue(System.getProperty("sun.java.command"));//应用程序参数
        vmCommand.setTooltiptext(vmCommand.getValue());
        totalThread.setValue(ManagementFactory.getThreadMXBean().getThreadCount() + "");//线程数

        gaugeChart.getExporting().setEnabled(false);//cpu及内存面板
        Pane pane1 = gaugeChart.getPane();
        pane1.setStartAngle(-55);
        pane1.setEndAngle(55);
        pane1.setBackground(new ArrayList<PaneBackground>());
        pane1.setCenter("25%", "145%");
        pane1.setSize(300);

        Pane pane2 = gaugeChart.getPane(1);
        pane2.setStartAngle(-55);
        pane2.setEndAngle(55);
        pane2.setBackground(new ArrayList<PaneBackground>());

        pane2.setCenter("75%", "145%");
        pane2.setSize(300);

        long heapSize = mmb.getHeapMemoryUsage().getMax() / 1024 / 1024;
        if (heapSize > 999) {
            heapSize = new BigDecimal((Math.floor(heapSize / 100) + 1) * 100).longValue();
        } else {
            heapSize = 1024;
        }
        YAxis yAxis1 = gaugeChart.getYAxis();
        yAxis1.setMin(0);
        yAxis1.setMax(heapSize);
        yAxis1.setMinorTickPosition("outside");
        yAxis1.setTickPosition("outside");
        List<Number> positions = new ArrayList<>();
        int increment = new BigDecimal(heapSize / 10).intValue();
        if (heapSize > 999)
            increment = increment - (increment % 100);
        else
            increment = increment - (increment % 50);
        for (double tick = 0; tick <= heapSize; tick += increment)
            positions.add(tick);
        yAxis1.setTickPositions(positions);
        yAxis1.getLabels().setRotation("auto");
        yAxis1.getLabels().setDistance(20);

        PlotBand plotBand = new PlotBand();
        plotBand.setFrom(0);
        plotBand.setTo(0.6 * heapSize);
        plotBand.setColor("#55BF3B");//绿
        plotBand.setInnerRadius("100%");
        plotBand.setOuterRadius("105%");
        yAxis1.addPlotBand(plotBand);

        plotBand = new PlotBand();
        plotBand.setFrom(0.6 * heapSize);
        plotBand.setTo(0.8 * heapSize);
        plotBand.setColor("#DDDF0D");//黄
        plotBand.setInnerRadius("100%");
        plotBand.setOuterRadius("105%");
        yAxis1.addPlotBand(plotBand);

        plotBand = new PlotBand();
        plotBand.setFrom(0.8 * heapSize);
        plotBand.setTo(heapSize);
        plotBand.setColor("#C02316");//红
        plotBand.setInnerRadius("100%");
        plotBand.setOuterRadius("105%");
        yAxis1.addPlotBand(plotBand);

        maxMemoryUsed = mmb.getHeapMemoryUsage().getMax() / 1024 / 1024;
        maxCpuUsed = osm.getProcessCpuLoad() * 100;
        yAxis1.setPane(0);
        yAxis1.setTitle("<strong>" + Labels.getLabel("admin.home.memory") + "(M)</strong><br/>" + Labels.getLabel("admin.jvm.now") + ":" + maxMemoryUsed + "M  " + Labels.getLabel("admin.jvm.max")  + ":" + maxMemoryUsed + "M");
        yAxis1.getTitle().setY(-40);

        YAxis yAxis2 = gaugeChart.getYAxis(1);
        yAxis2.setMin(0);
        yAxis2.setMax(100);
        yAxis2.setMinorTickPosition("outside");
        yAxis2.setTickPosition("outside");
        yAxis2.getLabels().setRotation("auto");
        List<Number> ps = new ArrayList<>();
        for (int i = 0; i <= 10; i++)
            ps.add(i * 10);
        yAxis2.setTickPositions(ps);
        yAxis2.getLabels().setDistance(20);

        plotBand = new PlotBand();
        plotBand.setFrom(0);
        plotBand.setTo(60);
        plotBand.setColor("#55BF3B");//绿
        plotBand.setInnerRadius("100%");
        plotBand.setOuterRadius("105%");
        yAxis2.addPlotBand(plotBand);

        plotBand = new PlotBand();
        plotBand.setFrom(60);
        plotBand.setTo(80);
        plotBand.setColor("#DDDF0D");//黄
        plotBand.setInnerRadius("100%");
        plotBand.setOuterRadius("105%");
        yAxis2.addPlotBand(plotBand);

        plotBand = new PlotBand();
        plotBand.setFrom(80);
        plotBand.setTo(100);
        plotBand.setColor("#C02316");//红
        plotBand.setInnerRadius("100%");
        plotBand.setOuterRadius("105%");
        yAxis2.addPlotBand(plotBand);

        yAxis2.setPane(1);
        yAxis2.setTitle("<strong>CPU(%)</strong><br/>" + Labels.getLabel("admin.jvm.now") + ":" + String.format("%.2f", maxCpuUsed) + "%  " + Labels.getLabel("admin.jvm.max") + ":" + String.format("%.2f", maxCpuUsed) + "%");
        yAxis2.getTitle().setY(-40);

        GaugePlotOptions plotOptions = gaugeChart.getPlotOptions().getGauge();
        plotOptions.getDataLabels().setEnabled(false);
        plotOptions.getDial().setRadius("100%");

        Series series1 = gaugeChart.getSeries();
        series1.setData(0);
        series1.setYAxis(0);

        Series series2 = gaugeChart.getSeries(1);
        series2.setData(0);
        series2.setYAxis(1);

        //cpu使用率
        Options options = new Options();
        options.getGlobal().setUseUTC(false);
        cpuChart.setOptions(options);
        cpuChart.setAnimation(true);

        cpuChart.getXAxis().setType("datetime");
        cpuChart.getXAxis().setTickPixelInterval(150);
        cpuChart.getYAxis().setMin(0);
        cpuChart.getYAxis().setMax(100);
        cpuChart.getYAxis().setTitle("");
        cpuChart.getYAxis().setTickInterval(10);
        cpuChart.getYAxis().getLabels().setFormat("{value}%");
        PlotLine plotLine = new PlotLine();
        plotLine.setValue(0);
        plotLine.setWidth(1);
        plotLine.setColor("#808080");
        cpuChart.getYAxis().addPlotLine(plotLine);
        cpuChart.getTooltip().setEnabled(false);

        cpuChart.getExporting().setEnabled(false);
        Legend legend = cpuChart.getLegend();
        legend.setLayout("vertical");
        legend.setAlign("right");
        legend.setVerticalAlign("top");
        legend.setY(0);
        legend.setFloating(true);

        Series jvmSeries = cpuChart.getSeries();
        jvmSeries.getMarker().setEnabled(false);
        jvmSeries.setName("JVM CPU Usage");
        jvmSeries.setColor("#32CD32");
        Series systemSeries = new Series();
        systemSeries.getMarker().setEnabled(false);
        systemSeries.setName("Machine CPU Usage");
        systemSeries.setColor("#436EEE");
        cpuChart.addSeries(systemSeries);
        for (int i = -19; i < 0; i++) {
            jvmSeries.addPoint(new Point(System.currentTimeMillis() + i * 1000, 0));
            systemSeries.addPoint(new Point(System.currentTimeMillis() + i * 1000, 0));
        }
        jvmSeries.addPoint(new Point(System.currentTimeMillis(), maxCpuUsed));
        systemSeries.addPoint(new Point(System.currentTimeMillis(), osm.getSystemCpuLoad() * 100));

        //memory使用率
        memoryChart.setAnimation(true);

        memoryChart.getXAxis().setType("datetime");
        memoryChart.getXAxis().setTickPixelInterval(150);
        memoryChart.getYAxis().setMin(0);
        memoryChart.getYAxis().setMax(heapSize);
        memoryChart.getYAxis().setTitle("");
        memoryChart.getYAxis().setTickInterval(increment);
        memoryChart.getYAxis().getLabels().setFormat("{value}M");
        //memoryChart.getYAxis().getLabels().setFormatter(new JavaScriptValue(""));
        memoryChart.getTooltip().setEnabled(false);

        memoryChart.getExporting().setEnabled(false);
        legend = memoryChart.getLegend();
        legend.setLayout("vertical");
        legend.setAlign("right");
        legend.setVerticalAlign("top");
        legend.setY(0);
        legend.setFloating(true);

        Series committedSeries = memoryChart.getSeries();
        committedSeries.getMarker().setEnabled(false);
        committedSeries.setName("Committed Java Heap");
        Series maxSeries = new Series();
        maxSeries.getMarker().setEnabled(false);
        maxSeries.setName("Maximum Java Heap");
        memoryChart.addSeries(maxSeries);
        Series usedJavaSeries = new Series();
        usedJavaSeries.getMarker().setEnabled(false);
        usedJavaSeries.setName("Used Java Heap Memory");
        memoryChart.addSeries(usedJavaSeries);
        Series usedPhysicalSeries = new Series();
        usedPhysicalSeries.getMarker().setEnabled(false);
        usedPhysicalSeries.setName("Used Physical Memory");
        memoryChart.addSeries(usedPhysicalSeries);
        for (int i = -19; i < 0; i++) {
            committedSeries.addPoint(new Point(System.currentTimeMillis() + i * 1000, 0));
            maxSeries.addPoint(new Point(System.currentTimeMillis() + i * 1000, 0));
            usedJavaSeries.addPoint(new Point(System.currentTimeMillis() + i * 1000, 0));
            usedPhysicalSeries.addPoint(new Point(System.currentTimeMillis() + i * 1000, 0));
        }
        committedSeries.addPoint(new Point(System.currentTimeMillis(), mmb.getHeapMemoryUsage().getCommitted() / 1024 / 1024));
        maxSeries.addPoint(new Point(System.currentTimeMillis(), mmb.getHeapMemoryUsage().getMax() / 1024 / 1024));
        usedJavaSeries.addPoint(new Point(System.currentTimeMillis(), mmb.getHeapMemoryUsage().getUsed() / 1024 / 1024));
        usedPhysicalSeries.addPoint(new Point(System.currentTimeMillis(), (osm.getTotalPhysicalMemorySize() - osm.getFreePhysicalMemorySize()) / 1024 / 1024));

        committedSeries.hide();
        maxSeries.hide();
        usedPhysicalSeries.hide();
        /*} else {
            MessageBox.showMessage(resp);
            comp.detach();
        }*/
    }

    @Listen("onPlotHide = #memoryChart")
    public void hideSeries(ChartsEvent event) {
        long heapSize = mmb.getHeapMemoryUsage().getMax() / 1024 / 1024;
        if (heapSize > 999) {
            heapSize = new BigDecimal((Math.floor(heapSize / 100) + 1) * 100).longValue();
        } else {
            heapSize = 1024;
        }
        event.getSeries().setVisible(false);
        if (!memoryChart.getSeries(3).isVisible()) {
            memoryChart.getYAxis().setMax(heapSize);
            int increment = new BigDecimal(heapSize / 10).intValue();
            if (heapSize > 999)
                increment = increment - (increment % 100);
            else
                increment = increment - (increment % 50);
            memoryChart.getYAxis().setTickInterval(increment);
        }
    }

    @Listen("onPlotShow = #memoryChart")
    public void showSeries(ChartsEvent event) {
        long heapSize = mmb.getHeapMemoryUsage().getMax() / 1024 / 1024;
        if (heapSize > 999) {
            heapSize = new BigDecimal((Math.floor(heapSize / 100) + 1) * 100).longValue();
        } else {
            heapSize = 1024;
        }
        event.getSeries().setVisible(true);
        if (event.getSeriesIndex() == 3) {//Used Physical Memory
            long usedPhysical = (osm.getTotalPhysicalMemorySize() - osm.getFreePhysicalMemorySize()) / 1024 / 1024;
            if (heapSize < usedPhysical) {
                memoryChart.getYAxis().setMax(usedPhysical);
                int increment = new BigDecimal(usedPhysical / 10).intValue();
                if (usedPhysical > 999)
                    increment = increment - (increment % 100);
                else
                    increment = increment - (increment % 50);
                memoryChart.getYAxis().setTickInterval(increment);
            }
        } else if (!memoryChart.getSeries(3).isVisible()) {
            memoryChart.getYAxis().setMax(heapSize);
            int increment = new BigDecimal(heapSize / 10).intValue();
            if (heapSize > 999)
                increment = increment - (increment % 100);
            else
                increment = increment - (increment % 50);
            memoryChart.getYAxis().setTickInterval(increment);
        }
    }

    @Listen("onTimer = #timer")
    public void updateData() {
        Point left = gaugeChart.getSeries().getPoint(0),
                right = gaugeChart.getSeries(1).getPoint(0);
        long memoryUsed = mmb.getHeapMemoryUsage().getUsed() / 1024 / 1024;
        if (memoryUsed > maxMemoryUsed)
            maxMemoryUsed = memoryUsed;
        left.update(memoryUsed);
        double cpuUsed = osm.getProcessCpuLoad() * 100;
        if (cpuUsed > maxCpuUsed)
            maxCpuUsed = cpuUsed;
        right.update(cpuUsed);
        YAxis yAxis1 = gaugeChart.getYAxis();
        yAxis1.setTitle("<strong>" +  Labels.getLabel("admin.home.memory")  + "(M)</strong><br/>" + Labels.getLabel("admin.jvm.now") + ":" + memoryUsed + "M  " + Labels.getLabel("admin.jvm.max") + ":" + maxMemoryUsed + "M");
        YAxis yAxis2 = gaugeChart.getYAxis(1);
        yAxis2.setTitle("<strong>CPU(%)</strong><br/>" +Labels.getLabel("admin.jvm.now") + ":" + String.format("%.2f", cpuUsed) + "%  " + Labels.getLabel("admin.jvm.max") + ":" + String.format("%.2f", maxCpuUsed) + "%");

        cpuChart.getSeries().addPoint(new Point(System.currentTimeMillis(), cpuUsed), true, true, true);
        cpuChart.getSeries(1).addPoint(new Point(System.currentTimeMillis(), osm.getSystemCpuLoad() * 100), true, true, true);


        memoryChart.getSeries(0).addPoint(new Point(System.currentTimeMillis(), mmb.getHeapMemoryUsage().getCommitted() / 1024 / 1024), true, true, true);
        memoryChart.getSeries(1).addPoint(new Point(System.currentTimeMillis(), mmb.getHeapMemoryUsage().getMax() / 1024 / 1024), true, true, true);
        memoryChart.getSeries(2).addPoint(new Point(System.currentTimeMillis(), mmb.getHeapMemoryUsage().getUsed() / 1024 / 1024), true, true, true);
        memoryChart.getSeries(3).addPoint(new Point(System.currentTimeMillis(), (osm.getTotalPhysicalMemorySize() - osm.getFreePhysicalMemorySize()) / 1024 / 1024), true, true, true);

        totalThread.setValue(ManagementFactory.getThreadMXBean().getThreadCount() + "");
        upTime.setValue(calculateInterval(rmb.getUptime()));
    }

    private String calculateInterval(long uptime) {
        if (uptime == 0) {
            return "0" + Labels.getLabel("admin.jvm.minute");
        }
        StringBuilder str = new StringBuilder();
        long p = Math.abs(uptime);
        long day = p / (24 * 3600000);
        if (day > 0) {
            str.append(day).append(Labels.getLabel("admin.jvm.day"));
        }
        p = p % (24 * 3600000);
        long hour = p / (3600000);
        if (hour > 0) {
            str.append(hour).append(Labels.getLabel("admin.jvm.hour"));
        }
        p = p % (3600000);
        long minute = p / (60000);
        if (minute > 0) {
            str.append(minute).append(Labels.getLabel("admin.jvm.minute"));
        }
        p = p % (60000);
        long second = p / (1000);
        if (second > 0) {
            str.append(second).append(Labels.getLabel("admin.jvm.second"));
        }
        return str.toString();
    }

    private String format(Object o1, Object o2) {
        BigDecimal b1 = new BigDecimal(o1.toString());
        BigDecimal b2 = new BigDecimal(o2.toString());
        return String.format("%.2f%%", ((b1.divide(b2, 4, BigDecimal.ROUND_HALF_UP)).doubleValue()) * 100);
    }

    private String format(long size) {
        if (size < 1024) {
            return size + "B";
        } else {
            size = size / 1024;
        }
        if (size < 1024) {
            return size + "KB";
        } else {
            size = size / 1024;
        }
        if (size < 1024) {
            size = size * 100;
            return (size / 100) + "."
                    + (size % 100) + "MB";
        } else {
            size = size * 100 / 1024;
            return (size / 100) + "."
                    + (size % 100) + "GB";
        }
    }
}